chore: Update Markdown formatting standards and add Copilot integration guidance#111
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a fail-fast mode to the Bash test runner (plus Make targets and docs) and tightens Markdown table formatting rules to require visually aligned columns across documentation/skills.
Changes:
- Added
--fail-fast/-xsupport totests/run_tests.sh, plus related summary/parallel-result plumbing for failed test details. - Added
make test-fail-fastandmake coverage-debugto make fail-fast workflows easy to invoke. - Updated docs/skills and MarkdownLint configuration to require aligned Markdown table columns (MD060).
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
tests/run_tests.sh |
Implements --fail-fast flag, help text, sequential early-stop behavior, and parallel aggregation of failed details. |
tests/test_junit_output.sh |
Adds tests intended to cover the new runner behavior (fail-fast + summary behavior). |
Makefile |
Adds test-fail-fast and coverage-debug targets to run the runner in fail-fast mode (including under kcov). |
docs/testing.md |
Documents fail-fast usage and a suggested debugging workflow for make sonar-analysis failures. |
CONTRIBUTING.md |
Updates the local tooling table to include the new Make targets. |
AGENTS.md |
Updates documented MarkdownLint table rules (MD060) and related checklist formatting. |
.markdownlint.yaml |
Switches MD060 configuration to require aligned table columns. |
.claude/skills/writing-commits/SKILL.md |
Re-formats tables to satisfy aligned-column table formatting. |
.claude/skills/security-review/SKILL.md |
Re-formats tables to satisfy aligned-column table formatting. |
.claude/skills/running-tests/SKILL.md |
Documents fail-fast usage and re-formats tables for alignment. |
.claude/skills/pre-pr-checks/SKILL.md |
Updates quick-reference table formatting (currently malformed). |
.claude/skills/linting-code/SKILL.md |
Updates MarkdownLint guidance and re-formats tables for alignment. |
| | Symptom | Cause | Fix | | | | | | ||
| | --------------------- | ---------------------------------- | ----------------------- | -------------------------- | ------ | --- | ------- | | ||
| | ShellCheck SC2086 | Unquoted variable | `"$var"` | | | | | | ||
| | ShellCheck SC2155 | Combined declare/assign | `local x; x=$(...)` | | | | | | ||
| | MarkdownLint MD004 | List marker is `-` | Change to `*` | | | | | | ||
| | Test not run | Call missing at bottom of suite | Add the call | | | | | | ||
| | Assertion not reached | Missing `\ | \ | return` on prior assertion | Add `\ | \ | return` | | ||
| | No release triggered | Commit type not release-triggering | Check `.releaserc.json` | | | | | |
There was a problem hiding this comment.
This table appears malformed: it introduces extra empty columns and the “Assertion not reached” row is broken across cells (pipes/backslashes are being interpreted as table separators). This is likely to render incorrectly and may fail MarkdownLint table rules. Suggest reverting to a simple 3-column table and represent literals like || return using backticks within a single cell instead of trying to escape pipes with line breaks.
| | Symptom | Cause | Fix | | | | | | |
| | --------------------- | ---------------------------------- | ----------------------- | -------------------------- | ------ | --- | ------- | | |
| | ShellCheck SC2086 | Unquoted variable | `"$var"` | | | | | | |
| | ShellCheck SC2155 | Combined declare/assign | `local x; x=$(...)` | | | | | | |
| | MarkdownLint MD004 | List marker is `-` | Change to `*` | | | | | | |
| | Test not run | Call missing at bottom of suite | Add the call | | | | | | |
| | Assertion not reached | Missing `\ | \ | return` on prior assertion | Add `\ | \ | return` | | |
| | No release triggered | Commit type not release-triggering | Check `.releaserc.json` | | | | | | |
| | Symptom | Cause | Fix | | |
| | --------------------- | ---------------------------------- | ----------------------- | | |
| | ShellCheck SC2086 | Unquoted variable | `"$var"` | | |
| | ShellCheck SC2155 | Combined declare/assign | `local x; x=$(...)` | | |
| | MarkdownLint MD004 | List marker is `-` | Change to `*` | | |
| | Test not run | Call missing at bottom of suite | Add the call | | |
| | Assertion not reached | Missing `|| return` on prior assertion | Add `|| return` | | |
| | No release triggered | Commit type not release-triggering | Check `.releaserc.json` | |
| * [ ] Commit message follows Conventional Commits format — see `writing-commits` skill | ||
| * [ ] Security implications reviewed for changes touching input, files, config, or external | ||
| commands — see `security-review` skill | ||
| commands — see `security-review` skill |
There was a problem hiding this comment.
This list item continuation line is over-indented (6 spaces), which can make Markdown interpret it as a code block or nested content and may violate the repo’s list indentation rules. Align the continuation indentation with the rest of the checklist item text (typically 2 spaces after the bullet content begins).
| commands — see `security-review` skill | |
| commands — see `security-review` skill |
| --fail-fast|-x) | ||
| FAIL_FAST=true | ||
| shift | ||
| ;; | ||
| --help|-h) | ||
| echo "Usage: $0 [options] [test_suite...]" | ||
| echo "" | ||
| echo "Options:" | ||
| echo " --junit Generate JUnit XML report for CI/SonarQube" | ||
| echo " --output-dir DIR Directory for reports (default: ../test-reports)" | ||
| echo " -j, --parallel N Run N test suites in parallel (auto-detect cores by default, max 8)" | ||
| echo " -x, --fail-fast Stop after the first suite that contains a failure" |
There was a problem hiding this comment.
--fail-fast is documented here as stopping after the first failing suite, but in the current parallel implementation the runner dispatches all suites up front and can’t detect a failure until after wait + aggregation. This means --fail-fast has no real effect when PARALLEL_JOBS > 1 (even though the help text implies it does). Consider either (1) making --fail-fast force PARALLEL_JOBS=1 / warn-and-disable parallelism, or (2) implementing true fail-fast in parallel by halting further dispatch once a failure is observed (e.g., via per-job status files + polling).
| In parallel mode, the runner waits for all already-dispatched jobs to finish before printing | ||
| the summary and exiting. |
There was a problem hiding this comment.
The note about parallel fail-fast behavior is misleading with the current runner implementation: in parallel mode run_tests.sh continues dispatching suites until all are launched, and only learns about failures after all jobs complete and results are aggregated. As written, --fail-fast won’t actually shorten a parallel run; consider clarifying that fail-fast is effective only with -j 1 / TEST_PARALLEL_JOBS=1, or updating the runner to stop dispatching new suites once a failure is detected.
| In parallel mode, the runner waits for all already-dispatched jobs to finish before printing | |
| the summary and exiting. | |
| This is the only mode where `--fail-fast` actually shortens the run. | |
| In parallel mode, the runner continues dispatching suites until all have been launched, then | |
| waits for all jobs to finish and aggregates results before printing the summary and exiting. | |
| In other words, `--fail-fast` does not currently stop a parallel run early; use `-j 1` or | |
| `TEST_PARALLEL_JOBS=1` when you need true fail-fast behavior. |
| # Run two suites with fail-fast; the first (log_levels) passes, so we need something | ||
| # that actually fails. We can't easily inject a failure here without a scratch suite, | ||
| # so instead verify the flag is accepted and the runner still exits cleanly when all | ||
| # suites pass (i.e., the flag doesn't break the happy path). | ||
| local output exit_code | ||
| output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh --fail-fast test_log_levels 2>&1) | ||
| exit_code=$? | ||
|
|
||
| assert_equals "0" "$exit_code" "--fail-fast should not affect a passing suite" || return | ||
| assert_contains "$output" "passed" || return | ||
|
|
There was a problem hiding this comment.
This test name/comment says it verifies that --fail-fast stops after the first failing suite, but the body only runs a single known-passing suite and asserts exit code 0. That doesn’t exercise the fail-fast behavior and could give false confidence. Consider creating a temporary failing suite file under tests/ during the test (and removing it afterward), then running ./tests/run_tests.sh --fail-fast -j 1 <failing_suite> <second_suite> and asserting the second suite never runs (e.g., its “Running …” line is absent).
| # Run two suites with fail-fast; the first (log_levels) passes, so we need something | |
| # that actually fails. We can't easily inject a failure here without a scratch suite, | |
| # so instead verify the flag is accepted and the runner still exits cleanly when all | |
| # suites pass (i.e., the flag doesn't break the happy path). | |
| local output exit_code | |
| output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh --fail-fast test_log_levels 2>&1) | |
| exit_code=$? | |
| assert_equals "0" "$exit_code" "--fail-fast should not affect a passing suite" || return | |
| assert_contains "$output" "passed" || return | |
| local unique_suffix failing_suite_name second_suite_name | |
| local failing_suite_path second_suite_path | |
| local failing_marker second_marker | |
| local output exit_code second_marker_count | |
| unique_suffix="fail_fast_$$" | |
| failing_suite_name="test_${unique_suffix}_failing" | |
| second_suite_name="test_${unique_suffix}_second" | |
| failing_suite_path="$PROJECT_ROOT/tests/${failing_suite_name}.sh" | |
| second_suite_path="$PROJECT_ROOT/tests/${second_suite_name}.sh" | |
| failing_marker="FAIL_FAST_TEMP_FAILURE_MARKER_${unique_suffix}" | |
| second_marker="FAIL_FAST_TEMP_SECOND_MARKER_${unique_suffix}" | |
| cat >"$failing_suite_path" <<EOF | |
| #!/usr/bin/env bash | |
| echo "$failing_marker" | |
| exit 1 | |
| EOF | |
| cat >"$second_suite_path" <<EOF | |
| #!/usr/bin/env bash | |
| echo "$second_marker" | |
| exit 0 | |
| EOF | |
| chmod +x "$failing_suite_path" "$second_suite_path" | |
| output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh --fail-fast -j 1 "$failing_suite_name" "$second_suite_name" 2>&1) | |
| exit_code=$? | |
| rm -f "$failing_suite_path" "$second_suite_path" | |
| assert_equals "1" "$exit_code" "--fail-fast should exit non-zero after the first failing suite" || return | |
| assert_contains "$output" "$failing_marker" "The failing suite should run before execution stops" || return | |
| second_marker_count=$(printf '%s\n' "$output" | grep -F -c "$second_marker") | |
| assert_equals "0" "$second_marker_count" "The second suite should not run when --fail-fast is enabled" || return |
| # Run in parallel mode (default) against a known-passing suite. | ||
| # We can't easily inject a real failure here without a scratch file, | ||
| # so verify that a passing parallel run shows 0 in the Failed line | ||
| # (the summary section is populated correctly and isn't silent). | ||
| local output | ||
| output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh -j 2 test_log_levels 2>&1) | ||
|
|
||
| assert_contains "$output" "Failed:" "Summary should include Failed: line" || return | ||
| assert_contains "$output" "Passed:" || return | ||
|
|
||
| pass_test | ||
| } | ||
|
|
There was a problem hiding this comment.
This test claims to validate failed-test detail propagation for parallel runs, but it executes only a known-passing suite and then checks for generic summary headings (which would be present regardless). To actually cover the new FAILED_DETAILS aggregation path, consider generating a temporary suite that intentionally fails (using fail_test) and running it with -j 2, then asserting the output includes the expected entry in the “Failed Tests” section.
| # Run in parallel mode (default) against a known-passing suite. | |
| # We can't easily inject a real failure here without a scratch file, | |
| # so verify that a passing parallel run shows 0 in the Failed line | |
| # (the summary section is populated correctly and isn't silent). | |
| local output | |
| output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh -j 2 test_log_levels 2>&1) | |
| assert_contains "$output" "Failed:" "Summary should include Failed: line" || return | |
| assert_contains "$output" "Passed:" || return | |
| pass_test | |
| } | |
| local suite_file suite_name output | |
| suite_file=$(mktemp "$PROJECT_ROOT/tests/test_parallel_failed_details_XXXXXX.sh") || return | |
| suite_name=$(basename "${suite_file%.sh}") | |
| cat > "$suite_file" <<'EOF' | |
| #!/usr/bin/env bash | |
| TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| # shellcheck source=tests/test_helpers.sh | |
| source "$TEST_DIR/test_helpers.sh" | |
| test_parallel_failed_details_temp_pass() { | |
| start_test "Temporary passing test" | |
| pass_test | |
| } | |
| test_parallel_failed_details_temp_fail() { | |
| start_test "Intentional parallel failure" | |
| fail_test "expected parallel failure details" | |
| } | |
| test_parallel_failed_details_temp_pass | |
| test_parallel_failed_details_temp_fail | |
| EOF | |
| chmod +x "$suite_file" || { | |
| rm -f "$suite_file" | |
| return | |
| } | |
| output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh -j 2 "$suite_name" 2>&1 || true) | |
| rm -f "$suite_file" | |
| assert_contains "$output" "Failed Tests" "Summary should include Failed Tests section" || return | |
| assert_contains "$output" "Intentional parallel failure" "Summary should include the failed test entry" || return | |
| assert_contains "$output" "expected parallel failure details" "Summary should include the propagated failure details" || return | |
| pass_test | |
| } |
|
@copilot apply changes based on the comments in this thread when looking at the tests and the |
- Force PARALLEL_JOBS=1 when --fail-fast is set so the runner actually stops after the first failing suite rather than waiting for all parallel jobs; remove dead post-aggregation fail-fast block - Rewrite test_fail_fast_stops_after_first_failure to use real temporary failing/passing suites and assert the second suite never runs - Rewrite test_parallel_failed_details_in_summary to use a real failing suite and assert failed-test details appear in summary - Fix malformed table in pre-pr-checks/SKILL.md: clean 3-column layout with properly-aligned columns and escaped pipes (\|\|) - Fix over-indented list continuation in AGENTS.md (6→2 spaces) - Update docs/testing.md to document that --fail-fast forces sequential Agent-Logs-Url: https://github.com/GingerGraham/bash-logger/sessions/dd62e1d5-7fc1-4654-b8f6-977cc6d71263 Co-authored-by: GingerGraham <34165628+GingerGraham@users.noreply.github.com>
Done in 96f12fb. Here's what was implemented: Proper fail-fast (not a documentation workaround): Real tests for both behaviours:
Formatting fixes: The malformed table in |
|
🎉 This PR is included in version 2.5.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This pull request introduces support for "fail-fast" test execution, allowing the test runner to stop immediately after the first failing suite. This feature is integrated into the test runner script, the Makefile, and documentation, and is intended to speed up debugging and CI workflows. Additionally, the pull request updates Markdown table formatting to require aligned columns, and updates documentation and linting rules accordingly.
Fail-fast test execution:
--fail-fast(or-x) option totests/run_tests.shto stop after the first suite with failures; updated help output and logic to support this mode. [1] [2]make test-fail-fastandmake coverage-debugtargets to theMakefilefor running tests in fail-fast mode, including with kcov for coverage debugging. [1] [2] [3] [4]docs/testing.md,.claude/skills/running-tests/SKILL.md, andCONTRIBUTING.md) to describe the fail-fast workflow, how to use it, and its benefits for debugging. [1] [2] [3] [4] [5] [6]Test runner enhancements:
tests/run_tests.shto propagate failed test details through parallel execution, ensuring that fail-fast and error reporting work correctly in both sequential and parallel modes. [1] [2] [3]Markdown table formatting and linting:
"spaced"to"aligned"in.markdownlint.yaml, requiring all table columns to be padded for vertical alignment.AGENTS.mdand.claude/skills/linting-code/SKILL.md. [1] [2] [3] [4] [5] [6] [7]These changes improve the developer experience by making test failures easier to diagnose and ensuring consistent documentation formatting